/*
 * @Author: sky 1575951471@qq.com
 * @Date: 2024-12-03 23:09:30
 * @LastEditors: sky 1575951471@qq.com
 * @LastEditTime: 2024-12-05 00:04:56
 * @FilePath: \sky-esp32-Base\main\APP\lvgl_config.c
 * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
 * 
 */
#include "lvgl_config.h"
#include "lcd.h"
#include "esp_timer.h"
#include "lvgl.h"
#include "FreeRTOSConfig.h"
#include "LVGL/demos/music/lv_demo_music.h"
#include "esp_log.h"

SemaphoreHandle_t xGuiSemaphore;
const char *lvgl_TAG = "lvgl_config";
uint16_t temp[2] = {0};


static void lvgl_disp_flush_cb(lv_disp_drv_t *drv, const lv_area_t *area, lv_color_t *color_map)
{
    lcd_fill(area->x1, area->y1, area->x2, area->y2, (uint16_t*)color_map);                         //显示屏区域更新像素点颜色，像素颜色在数组中
    lv_disp_flush_ready(drv); //发送更新完毕，这一句代码必须要有
}

static void increase_lvgl_tick(void *arg)
{
    lv_tick_inc(1);
}

void lv_port_disp_init(void)
{
    void *buf1 = NULL;
    // void *buf2 = NULL;
    static lv_disp_draw_buf_t   disp_buf;               /*保存显示缓冲区信息的结构体*/
    static lv_disp_drv_t        disp_drv;               /*显示设备的描述符*/

    lcd_init();                 /*LCD屏幕初始化*/
    /*-----------------------------
     * 创建一个绘图缓冲区
     *----------------------------*/
    /**
     * LVGL 需要一个缓冲区用来绘制小部件
     * 随后，这个缓冲区的内容会通过显示设备的 'flush_cb'(显示设备刷新函数) 复制到显示设备上
     * 这个缓冲区的大小需要大于显示设备一行的大小
     *
     * 这里有3种缓冲配置:
     * 1. 单缓冲区:
     *      LVGL 会将显示设备的内容绘制到这里，并将他写入显示设备。
     *
     * 2. 双缓冲区:
     *      LVGL 会将显示设备的内容绘制到其中一个缓冲区，并将他写入显示设备。
     *      需要使用 DMA 将要显示在显示设备的内容写入缓冲区。
     *      当数据从第一个缓冲区发送时，它将使 LVGL 能够将屏幕的下一部分绘制到另一个缓冲区。
     *      这样使得渲染和刷新可以并行执行。
     *
     * 3. 全尺寸双缓冲区
     *      设置两个屏幕大小的全尺寸缓冲区，并且设置 disp_drv.full_refresh = 1。
     *      这样，LVGL将始终以 'flush_cb' 的形式提供整个渲染屏幕，您只需更改帧缓冲区的地址。
     */
    /*使用双缓冲*/
    buf1 = heap_caps_malloc(lcd_self.width * 10 * sizeof(lv_color_t), MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
    // buf2 = heap_caps_malloc(lcd_self.width * 60 * sizeof(lv_color_t), MALLOC_CAP_DMA);

    /*初始化显示缓冲区*/
    lv_disp_draw_buf_init(&disp_buf, buf1, NULL, lcd_self.width * 10);

    /*在LVGL中注册显示设备*/
    lv_disp_drv_init(&disp_drv);

    /* 设置显示设备的分辨率 
     * 在实际项目中，通常所使用的屏幕大小是固定的，因此可以直接设置为屏幕的大小 
     */
    disp_drv.hor_res = lcd_self.width;
    disp_drv.ver_res = lcd_self.height;

    disp_drv.flush_cb = lvgl_disp_flush_cb;

    disp_drv.draw_buf = &disp_buf;

    // disp_drv.user_data = panel_handle
    lv_disp_drv_register(&disp_drv);

}

void lvgl_demo(void)
{
    lv_init();            //LVGL初始化前都需要调用
    lv_port_disp_init();  //显示驱动的移植，初始化及配对,输出  
    // lv_port_indev_init(); //输入驱动的移植

    xGuiSemaphore = xSemaphoreCreateMutex();

    const esp_timer_create_args_t lvgl_tick_timer_args = {
        .callback = &increase_lvgl_tick,
        .name = "lvgl_tick"
    };
    esp_timer_handle_t lvgl_tick_timer = NULL;
    ESP_ERROR_CHECK(esp_timer_create(&lvgl_tick_timer_args, &lvgl_tick_timer));
    ESP_ERROR_CHECK(esp_timer_start_periodic(lvgl_tick_timer, 1 * 1000)); //创建定时器，更新LVGL的内部时钟基准

    lv_demo_music();
    while(1)
    {
        if( pdTRUE == xSemaphoreTake(xGuiSemaphore, portMAX_DELAY))
        {
            lv_timer_handler(); //定时调用，更新LVGL的实现
            xSemaphoreGive(xGuiSemaphore);
        }
        
        vTaskDelay(pdMS_TO_TICKS(5));
    }
}